#ifndef UAPI_LIBSEMINIX_IPC_TYPES_H
#define UAPI_LIBSEMINIX_IPC_TYPES_H

#include <utils/bits.h>
#include <utils/bitops.h>

/*
 * +----------------------+-------+----------+
 * |xx       label       9|8 cap 7|6 length 0|
 * +----------------------+-------+----------+
 */
typedef unsigned long seminix_message_t;

#define SEMINIX_MSG_LENGTH_BITS    7
#define SEMINIX_MSG_LENGTH_SHIFT   0
#define SEMINIX_MSG_LENGTH_MASK    BIT_GENMASK(SEMINIX_MSG_LENGTH_BITS)
#define SEMINIX_MSG_LENGTH_MAX     BIT(SEMINIX_MSG_LENGTH_BITS)

#define SEMINIX_CAP_BITS           2
#define SEMINIX_CAP_SHIFT          (SEMINIX_MSG_LENGTH_SHIFT + SEMINIX_MSG_LENGTH_BITS)
#define SEMINIX_CAP_MASK           BIT_GENMASK(SEMINIX_CAP_BITS)
#define SEMINIX_CAP_MAX            BIT(SEMINIX_CAP_BITS)

#define SEMINIX_MSG_LABEL_BITS     ((sizeof(unsigned long)*8) - (SEMINIX_MSG_LENGTH_BITS + SEMINIX_CAP_BITS))
#define SEMINIX_MSG_LABEL_SHIFT    (SEMINIX_CAP_SHIFT + SEMINIX_CAP_BITS)
#define SEMINIX_MSG_LABEL_MASK     BIT_GENMASK(SEMINIX_MSG_LABEL_BITS)
#define SEMINIX_MSG_LABEL_MAX      BIT(SEMINIX_MSG_LABEL_BITS)

static inline seminix_message_t __attribute_const__
seminix_message_new(unsigned long label, int cap_length, int length)
{
    seminix_message_t mess = 0;

    assert((label & ~SEMINIX_MSG_LABEL_MASK) == 0);
    assert((cap_length & ~SEMINIX_CAP_MASK) == 0);
    assert((length & ~SEMINIX_MSG_LENGTH_MASK) == 0);

    mess = ((label & SEMINIX_MSG_LABEL_MASK) << SEMINIX_MSG_LABEL_SHIFT) |
             ((cap_length & SEMINIX_CAP_MASK) << SEMINIX_CAP_SHIFT) |
             ((length & SEMINIX_MSG_LENGTH_MASK) << SEMINIX_MSG_LENGTH_SHIFT);

    return mess;
}

static inline unsigned long __attribute_const__
seminix_message_get_label(seminix_message_t mess)
{
    return (mess >> SEMINIX_MSG_LABEL_SHIFT) & SEMINIX_MSG_LABEL_MASK;
}

static inline seminix_message_t __attribute_const__
seminix_message_set_label(seminix_message_t mess, unsigned long label)
{
    assert((label & ~SEMINIX_MSG_LABEL_MASK) == 0);

    mess &= ~(SEMINIX_MSG_LABEL_MASK << SEMINIX_MSG_LABEL_SHIFT);
    mess |= (label & SEMINIX_MSG_LABEL_MASK) << SEMINIX_MSG_LABEL_SHIFT;

    return mess;
}

static inline int __attribute_const__
seminix_message_get_cap_length(seminix_message_t mess)
{
    return (mess >> SEMINIX_CAP_SHIFT) & SEMINIX_CAP_MASK;
}

static inline seminix_message_t __attribute_const__
seminix_message_set_cap_length(seminix_message_t mess, int cap_length)
{
    assert((cap_length & ~SEMINIX_CAP_MASK) == 0);

    mess &= ~(SEMINIX_CAP_MASK << SEMINIX_CAP_SHIFT);
    mess |= (cap_length & SEMINIX_CAP_MASK) << SEMINIX_CAP_SHIFT;

    return mess;
}

static inline int __attribute_const__
seminix_message_get_length(seminix_message_t mess)
{
    return mess & SEMINIX_MSG_LENGTH_MASK;
}

static inline seminix_message_t __attribute_const__
seminix_message_set_length(seminix_message_t mess, int length)
{
    assert((length & ~SEMINIX_MSG_LENGTH_MASK) == 0);

    mess &= ~SEMINIX_MSG_LENGTH_MASK;
    mess |= (length & SEMINIX_MSG_LENGTH_MASK);

    return mess;
}

struct seminix_ipc_notify {
    unsigned long badge;
    seminix_message_t message;
};

typedef struct seminix_ipc_buffer {
    seminix_message_t message;
    uintptr_t msg[SEMINIX_MSG_LENGTH_MAX];
    unsigned long badge;
    bool   reply;
    int capdesc[SEMINIX_CAP_MAX];
#define IPC_NOTIFY_MAX  24
    struct seminix_ipc_notify notify[IPC_NOTIFY_MAX];
#define IPC_IRQ_NOTIFY_MAX  16
    seminix_message_t irqnotify[IPC_IRQ_NOTIFY_MAX];
} seminix_ipc_buffer_t;

#define IPC_IDLE           0
#define IPC_SEND           1
#define IPC_NBSEND         2
#define IPC_SEND_REPLY     3
#define IPC_NBSEND_REPLY   4
#define IPC_SEND_NOTIFY    5

#define IPC_RECV           6
#define IPC_NBRECV         7

#define IPC_INTR           8

#define SEMINIX_NOTIFY_NORMAL      1
#define SEMINIX_NOTIFY_IRQ         2

#endif /* !UAPI_LIBSEMINIX_IPC_TYPES_H */
